/*
 * This file or a portion of this file is licensed under the terms of
 * the Globus Toolkit Public License, found in file GTPL, or at
 * http://www.globus.org/toolkit/download/license.html. This notice must
 * appear in redistributions of this file, with or without modification.
 *
 * Redistributions of this Software, with or without modification, must
 * reproduce the GTPL in: (1) the Software, or (2) the Documentation or
 * some other similar material which is provided with the Software (if
 * any).
 *
 * Copyright 1999-2004 University of Chicago and The University of
 * Southern California. All rights reserved.
 */

package edu.isi.ikcap.workflows.util.logging;

/**
 * This class tries to define an interface to deal with quoting, escaping,
 * and the way back. The quoting algorithm is safe to only itself. Thus,
 *
 * <pre>
 * unescape( escape( s ) ) === s
 * </pre>
 *
 * holds true, but 
 *
 * <pre>
 * escape( unescape( s ) ) =?= s
 * </pre>
 * 
 * does not necessarily hold. 
 *
 * @author Gaurang Mehta
 * @author Karan Vahi
 * @author Jens-S. Vöckler
 * @version $Revision: 1.1 $
 */
public class Escape 
{
  /**
   * Defines the character used to escape characters.
   */
  private char m_escape;

  /**
   * Defines the set of characters that require escaping.
   */
  private String m_escapable;

  /**
   * Defines the default quoting and escaping rules, escaping the
   * apostrophe, double quote and backslash. The escape character
   * is the backslash.
   *
   */
  public Escape() 
  {
    m_escapable = "\"'\\";
    m_escape = '\\';
  }

  /**
   * Constructs arbitrary escaping rules.
   *
   * @param escapable is the set of characters that require escaping
   * @param escape is the escape character itself.
   */
  public Escape( String escapable, char escape )
  { 
    this( escapable, escape, true );
  }

  
  /**
   * Constructs arbitrary escaping rules.
   *
   * @param escapable is the set of characters that require escaping
   * @param escape is the escape character itself.
   * @param escapeEscape boolean indicating whether escape character itself
   *                     should be escaped if not present in escapable.
   */
  public Escape( String escapable, char escape , boolean escapeEscape )
  { 
    m_escape = escape;
    m_escapable = escapable;

    if( escapeEscape ){
        // ensure that the escape character is part of the escapable char set
        if ( escapable.indexOf(escape) == -1 ) m_escapable += m_escape; 
    }
  }
  
  /**
   * Transforms a given string by escaping all characters inside the
   * quotable characters set with the escape character. The escape
   * character itself is also escaped.
   *
   * @param s is the string to escape.
   * @return the quoted string
   * @see #unescape( String )
   */
  public String escape( String s )
  {
    // sanity check
    if ( s == null ) return null;

    StringBuffer result = new StringBuffer( s.length() );
    for ( int i=0; i<s.length(); ++i ) {
      char ch = s.charAt(i);
      if ( m_escapable.indexOf(ch) != -1 ) result.append( m_escape );
      result.append(ch);
    }
    return result.toString();
  }

  /**
   * Transforms a given string by unescaping all characters that
   * are prefixed with the escape character. 
   *
   * @param s is the string to remove escapes from.
   * @return the quoted string
   * @see #unescape( String )
   */
  public String unescape( String s )
  {
    // sanity check
    if ( s == null ) return null;

    StringBuffer result = new StringBuffer( s.length() );
    int state = 0;
    for ( int i=0; i<s.length(); ++i ) {
      char ch = s.charAt(i);
      if ( state == 0 ) {
	// default state
	if ( ch == m_escape ) state = 1;
	else result.append(ch);
      } else { 
	// "found escape" state
	if ( m_escapable.indexOf(ch) == -1 ) result.append(m_escape);
	result.append(ch);
	state = 0;
      }
    }
    
    return result.toString();
  }

  /**
   * Test program.
   *
   * @param args are command-line arguments
   */
  public static void main( String args[] ) 
  {
    Escape me = new Escape(); // defaults

    for ( int i=0; i<args.length; ++i ) {
      String e = me.escape(args[i]);
      String u = me.unescape(args[i]);
      System.out.println( "raw s  > " + args[i] );
      System.out.println( "e(s)   > " + e );
      System.out.println( "u(e(s))> " + me.unescape(e) );
      System.out.println( "u(s)   > " + u );
      System.out.println( "e(u(s))> " + me.escape(u) );
      System.out.println();
    }
  }
}
